#include "soundmanager.h"

CSoundManager *CSoundManager::getInstance()
{
    static CSoundManager s_instance;
    return &s_instance;
}

CSoundManager::CSoundManager()
    : m_pSoundManager(nullptr),
    m_fMasterVolume(0.3f),
    m_bMute(false)
{
    /**
     * COnstructor, ensure we intiialise the sound maanger 
     */
    System_Create(&m_pSoundManager);
    if (m_pSoundManager->init(10, FMOD_INIT_NORMAL, 0))
    {
        stringstream sstream;
        sstream << "CSoundManager::CSoundManager() - "
                << "Could not initialise the FMOD sound manager";
        m_pSoundManager = nullptr;
        MessageBox(nullptr, sstream.str().c_str(), "FMOD Error", MB_OK);
    }
}

CSoundManager::~CSoundManager()
{
    /**
     * Destructor, clear up all sounds and release the fmod system manager
     * memeory.
     */
    for (map<string, CSound*>::const_iterator pSound(m_arrSounds.begin());
         pSound != m_arrSounds.end(); ++pSound)
    {
        delete pSound->second;
    }
    if (m_pSoundManager != nullptr)
    {
        m_pSoundManager->release();
    }
}

CSound *CSoundManager::addStream(const string &szPath)
{
    CSound *pSound(getSound(szPath));
    if (pSound == nullptr)
    {
        pSound = new CSound(m_pSoundManager, szPath, true);
        pSound->setMasterVolume(getMasterVolume());
        pSound->setMute(isMuted());
        m_arrSounds.insert(pair<string, CSound*>(szPath, pSound));
    }
    return pSound;
}

CSound *CSoundManager::addSound(const string &szPath)
{
    CSound *pSound(getSound(szPath));
    if (pSound == nullptr)
    {
        pSound = new CSound(m_pSoundManager, szPath, false);
        pSound->setMasterVolume(getMasterVolume());
        pSound->setMute(isMuted());
        m_arrSounds.insert(pair<string, CSound*>(szPath, pSound));
    }
    return pSound;
}

CSound *CSoundManager::getSound(const string &szPath) const
{
    /**
     * @param const string &szPath The path of the sound
     * @return CSound* A pointer to the sound at the path (nullptr if it doesn't
     *  exist.
     */
    map<string, CSound*>::const_iterator pPos(m_arrSounds.find(szPath));
    if (pPos == m_arrSounds.end())
    {
        return nullptr;
    }
    return pPos->second;
}

bool CSoundManager::hasSound(const string &szPath) const
{
    /**
     * @param const string &szPath The reference of the sound to remove
     * @return bool Whetehr or not we have the sound
     */
    return getSound(szPath) != nullptr;
}

bool CSoundManager::removeSound(const string &szPath)
{
    /**
     * @param const string &szPath The reference of the sound to remove
     * @return bool WHether or not it was successful
     */
    map<string, CSound*>::const_iterator pPos(m_arrSounds.find(szPath));
    if (pPos != m_arrSounds.end())
    {
        delete pPos->second;
        m_arrSounds.erase(pPos);
        return true;
    }
    return false;
}

float CSoundManager::increaseMasterVolume()
{
    /**
     * Increases the master volume by 0.1. If it goes over 1, reset back
     * to 0.
     *
     * @return float The new master volume.
     */
    m_fMasterVolume = (m_fMasterVolume < 1.0f ? m_fMasterVolume + 0.1f :
        0.0f);
    setMasterVolume(m_fMasterVolume);
    return m_fMasterVolume;
}

float CSoundManager::getMasterVolume()
{
    /*
     * @return float The master volume
     */
    return m_fMasterVolume;
}

void CSoundManager::setMasterVolume(float fMasterVolume)
{
    /**
     * @param float fMasterVolume The new master volume
     */
    m_fMasterVolume = fMasterVolume;
    for (map<string, CSound*>::const_iterator pSound(m_arrSounds.begin());
         pSound != m_arrSounds.end(); ++pSound)
    {
        pSound->second->setMasterVolume(m_fMasterVolume);
    }
}

bool CSoundManager::isMuted() const
{
    return m_bMute;
}

void CSoundManager::setMute(bool bMute)
{
    m_bMute = bMute;
    for (map<string, CSound*>::const_iterator pSound(m_arrSounds.begin());
        pSound != m_arrSounds.end(); ++pSound)
    {
        pSound->second->setMute(m_bMute);
    }
}